<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>报警数据接口测试工具</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        danger: '#EF4444',
                        dark: '#1E293B',
                        light: '#F8FAFC'
                    },
                    fontFamily: {
                        inter: ['Inter', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .scrollbar-thin {
                scrollbar-width: thin;
            }
            .scrollbar-hide::-webkit-scrollbar {
                display: none;
            }
            .editor-shadow {
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
            }
            .card-hover {
                transition: all 0.3s ease;
            }
            .card-hover:hover {
                transform: translateY(-2px);
                box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-inter text-dark">
    <div class="min-h-screen flex flex-col">
        <!-- 顶部导航栏 -->
        <header class="bg-white shadow-sm">
            <div class="container mx-auto px-4 py-4 flex justify-between items-center">
                <div class="flex items-center space-x-2">
                    <i class="fa fa-bell text-primary text-2xl"></i>
                    <h1 class="text-xl font-bold text-dark">报警数据接口测试工具</h1>
                </div>
                <div class="flex items-center space-x-4">
                    <button id="save-btn" class="px-3 py-1.5 text-sm bg-secondary text-white rounded-md transition-all hover:bg-secondary/90 flex items-center">
                        <i class="fa fa-save mr-1"></i> 保存配置
                    </button>
                    <button id="reset-btn" class="px-3 py-1.5 text-sm bg-gray-200 text-gray-700 rounded-md transition-all hover:bg-gray-300 flex items-center">
                        <i class="fa fa-refresh mr-1"></i> 重置
                    </button>
                </div>
            </div>
        </header>

        <!-- 主要内容区 -->
        <main class="flex-grow container mx-auto px-4 py-6">
            <!-- 接口配置区 -->
            <div class="bg-white rounded-lg shadow-md p-5 mb-6">
                <h2 class="text-lg font-semibold mb-4 flex items-center">
                    <i class="fa fa-cog text-primary mr-2"></i> 接口配置
                </h2>
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                    <div class="col-span-2">
                        <label class="block text-sm font-medium text-gray-700 mb-1">接口URL</label>
                        <div class="flex">
                            <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm">
                                POST
                            </span>
                            <input type="text" id="api-url" value="http://localhost:8080/api/alarm/receive" 
                                class="flex-1 block w-full rounded-r-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary/20 transition-all" />
                        </div>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">超时设置(ms)</label>
                        <input type="number" id="timeout" value="10000" 
                            class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring focus:ring-primary/20 transition-all" />
                    </div>
                </div>
            </div>

            <!-- 请求内容区 -->
            <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
                <!-- 请求体编辑区 -->
                <div class="bg-white rounded-lg shadow-md overflow-hidden">
                    <div class="bg-gray-50 border-b border-gray-200 px-4 py-3 flex justify-between items-center">
                        <h2 class="text-lg font-semibold flex items-center">
                            <i class="fa fa-code text-primary mr-2"></i> 请求体 (JSON)
                        </h2>
                        <div class="flex space-x-2">
                            <button id="format-btn" class="text-xs px-2 py-1 bg-gray-200 rounded hover:bg-gray-300 transition-all flex items-center">
                                <i class="fa fa-indent mr-1"></i> 格式化
                            </button>
                            <button id="clear-btn" class="text-xs px-2 py-1 bg-gray-200 rounded hover:bg-gray-300 transition-all flex items-center">
                                <i class="fa fa-trash-o mr-1"></i> 清空
                            </button>
                        </div>
                    </div>
                    <div class="p-4">
                        <textarea id="request-body" rows="20" class="w-full h-full font-mono text-sm bg-gray-50 border border-gray-300 rounded-md p-3 scrollbar-thin focus:border-primary focus:ring focus:ring-primary/20 transition-all" placeholder="在此输入JSON请求体...">
{
    "controlCode": "control96009eb847",
    "flowMode": 1,
    "flowCode": "flow3359751e88",
    "flowName": "周界入侵算法",
    "drawType": 1,
    "flag": "20240924185747",
    "streamApp": "live",
    "streamName": "test",
    "streamCode": "test",
    "streamNickname": "测试摄像头",
    "desc": "周界检测到人员入侵",
    "videoCount": 1,
    "videoArray": [
        {
            "index": 0,
            "videoPath": "alarm/control96009eb847/20240919/140808/1/20240919140808.mp4",
            "videoUrl": "/storage/access?filename=alarm/control96009eb847/20240919/140808/1/20240919140808.mp4",
            "base64Str": "not enabled"
        }
    ],
    "imageCount": 1,
    "imageArray": [
        {
            "index": 0,
            "imagePath": "alarm/control96009eb847/20240919/140808/1/20240919140808.jpg",
            "imageUrl": "/storage/access?filename=alarm/control96009eb847/20240919/140808/1/20240919140808.jpg",
            "base64Str": "not enabled"
        }
    ],
    "imageDetects": [
        [
            {
                "x1": 843,
                "x2": 972,
                "y1": 764,
                "y2": 1036,
                "class_id": 0,
                "class_name": "person",
                "class_score": 0.6867910623550415,
                "region_index": 0,
                "keypoints": "",
                "keypoint_count": 0,
                "classify_class_id": -1,
                "classify_class_name": "",
                "classify_class_score": 0.0,
                "ocr_type": -1,
                "ocr_text": "",
                "ocr_text_score": -1,
                "ocr_text_idx": "",
                "ocr_color": -1,
                "track_cx": 0,
                "track_cy": 0,
                "track_id": -1,
                "track_last_cx": 0,
                "track_last_cy": 0,
                "track_max_similary": 0,
                "track_max_code": 0,
                "track_max_custom_code": 0
            }
        ]
    ],
    "extendParams": {
        "version": "4.503",
        "flag": "pub20240113"
    },
    "createTime": "2024/09/24 16:18:10"
}
                        </textarea>
                    </div>
                </div>

                <!-- 响应结果区 -->
                <div class="bg-white rounded-lg shadow-md overflow-hidden">
                    <div class="bg-gray-50 border-b border-gray-200 px-4 py-3 flex justify-between items-center">
                        <h2 class="text-lg font-semibold flex items-center">
                            <i class="fa fa-reply text-primary mr-2"></i> 响应结果
                        </h2>
                        <div class="flex space-x-2">
                            <div id="response-status" class="text-xs px-2 py-1 rounded bg-gray-200">
                                等待请求...
                            </div>
                            <button id="copy-response-btn" class="text-xs px-2 py-1 bg-gray-200 rounded hover:bg-gray-300 transition-all flex items-center">
                                <i class="fa fa-copy mr-1"></i> 复制
                            </button>
                        </div>
                    </div>
                    <div class="p-4">
                        <div id="response-headers" class="mb-3 bg-gray-50 p-2 rounded text-xs font-mono">
                            响应头: 等待请求...
                        </div>
                        <pre id="response-body" class="w-full h-[450px] bg-gray-50 border border-gray-300 rounded-md p-3 overflow-auto font-mono text-sm scrollbar-thin">
等待请求...
                        </pre>
                    </div>
                </div>
            </div>

            <!-- 请求统计区 -->
            <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
                <div class="bg-white rounded-lg shadow-sm p-4 card-hover">
                    <div class="flex items-center">
                        <div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center mr-3">
                            <i class="fa fa-paper-plane text-primary"></i>
                        </div>
                        <div>
                            <p class="text-sm text-gray-500">总请求次数</p>
                            <p id="total-requests" class="text-2xl font-bold">0</p>
                        </div>
                    </div>
                </div>
                <div class="bg-white rounded-lg shadow-sm p-4 card-hover">
                    <div class="flex items-center">
                        <div class="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center mr-3">
                            <i class="fa fa-check text-secondary"></i>
                        </div>
                        <div>
                            <p class="text-sm text-gray-500">成功次数</p>
                            <p id="success-requests" class="text-2xl font-bold">0</p>
                        </div>
                    </div>
                </div>
                <div class="bg-white rounded-lg shadow-sm p-4 card-hover">
                    <div class="flex items-center">
                        <div class="w-10 h-10 rounded-full bg-red-100 flex items-center justify-center mr-3">
                            <i class="fa fa-times text-danger"></i>
                        </div>
                        <div>
                            <p class="text-sm text-gray-500">失败次数</p>
                            <p id="failed-requests" class="text-2xl font-bold">0</p>
                        </div>
                    </div>
                </div>
                <div class="bg-white rounded-lg shadow-sm p-4 card-hover">
                    <div class="flex items-center">
                        <div class="w-10 h-10 rounded-full bg-yellow-100 flex items-center justify-center mr-3">
                            <i class="fa fa-clock-o text-yellow-500"></i>
                        </div>
                        <div>
                            <p class="text-sm text-gray-500">平均响应时间</p>
                            <p id="avg-response-time" class="text-2xl font-bold">0 ms</p>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 发送请求按钮 -->
            <div class="flex justify-center">
                <button id="send-request-btn" class="px-6 py-3 bg-primary hover:bg-primary/90 text-white rounded-lg shadow-md transition-all flex items-center">
                    <i class="fa fa-paper-plane mr-2"></i> 发送请求
                </button>
            </div>
        </main>

        <!-- 页脚 -->
        <footer class="bg-white border-t border-gray-200 py-4">
            <div class="container mx-auto px-4 text-center text-sm text-gray-500">
                <p>© 2024 报警数据接口测试工具 | 版本 1.0.0</p>
            </div>
        </footer>
    </div>

    <!-- 成功提示 -->
    <div id="success-toast" class="fixed bottom-4 right-4 bg-secondary text-white px-4 py-2 rounded shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
        <i class="fa fa-check-circle mr-2"></i>
        <span id="success-message">操作成功</span>
    </div>

    <!-- 错误提示 -->
    <div id="error-toast" class="fixed bottom-4 right-4 bg-danger text-white px-4 py-2 rounded shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
        <i class="fa fa-exclamation-circle mr-2"></i>
        <span id="error-message">操作失败</span>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 获取DOM元素
            const apiUrlInput = document.getElementById('api-url');
            const timeoutInput = document.getElementById('timeout');
            const requestBodyTextarea = document.getElementById('request-body');
            const responseBodyPre = document.getElementById('response-body');
            const responseHeadersDiv = document.getElementById('response-headers');
            const responseStatusDiv = document.getElementById('response-status');
            const sendRequestBtn = document.getElementById('send-request-btn');
            const formatBtn = document.getElementById('format-btn');
            const clearBtn = document.getElementById('clear-btn');
            const resetBtn = document.getElementById('reset-btn');
            const saveBtn = document.getElementById('save-btn');
            const copyResponseBtn = document.getElementById('copy-response-btn');
            const successToast = document.getElementById('success-toast');
            const errorToast = document.getElementById('error-toast');
            const successMessage = document.getElementById('success-message');
            const errorMessage = document.getElementById('error-message');
            const totalRequestsEl = document.getElementById('total-requests');
            const successRequestsEl = document.getElementById('success-requests');
            const failedRequestsEl = document.getElementById('failed-requests');
            const avgResponseTimeEl = document.getElementById('avg-response-time');

            // 统计数据
            let stats = {
                totalRequests: 0,
                successRequests: 0,
                failedRequests: 0,
                totalResponseTime: 0
            };

            // 从本地存储加载配置
            loadConfig();

            // 格式化JSON按钮
            formatBtn.addEventListener('click', function() {
                try {
                    const parsed = JSON.parse(requestBodyTextarea.value.trim() || '{}');
                    requestBodyTextarea.value = JSON.stringify(parsed, null, 2);
                    showSuccessToast('JSON格式化成功');
                } catch (e) {
                    showErrorToast('JSON格式错误: ' + e.message);
                }
            });

            // 清空请求体按钮
            clearBtn.addEventListener('click', function() {
                requestBodyTextarea.value = '';
                showSuccessToast('请求体已清空');
            });

            // 重置按钮
            resetBtn.addEventListener('click', function() {
                apiUrlInput.value = 'http://localhost:8080/api/alarm/receive';
                timeoutInput.value = 10000;
                requestBodyTextarea.value = `{
    "controlCode": "control96009eb847",
    "flowMode": 1,
    "flowCode": "flow3359751e88",
    "flowName": "周界入侵算法",
    "drawType": 1,
    "flag": "20240924185747",
    "streamApp": "live",
    "streamName": "test",
    "streamCode": "test",
    "streamNickname": "测试摄像头",
    "desc": "周界检测到人员入侵",
    "videoCount": 1,
    "videoArray": [
        {
            "index": 0,
            "videoPath": "alarm/control96009eb847/20240919/140808/1/20240919140808.mp4",
            "videoUrl": "/storage/access?filename=alarm/control96009eb847/20240919/140808/1/20240919140808.mp4",
            "base64Str": "not enabled"
        }
    ],
    "imageCount": 1,
    "imageArray": [
        {
            "index": 0,
            "imagePath": "alarm/control96009eb847/20240919/140808/1/20240919140808.jpg",
            "imageUrl": "/storage/access?filename=alarm/control96009eb847/20240919/140808/1/20240919140808.jpg",
            "base64Str": "not enabled"
        }
    ],
    "imageDetects": [
        [
            {
                "x1": 843,
                "x2": 972,
                "y1": 764,
                "y2": 1036,
                "class_id": 0,
                "class_name": "person",
                "class_score": 0.6867910623550415,
                "region_index": 0,
                "keypoints": "",
                "keypoint_count": 0,
                "classify_class_id": -1,
                "classify_class_name": "",
                "classify_class_score": 0.0,
                "ocr_type": -1,
                "ocr_text": "",
                "ocr_text_score": -1,
                "ocr_text_idx": "",
                "ocr_color": -1,
                "track_cx": 0,
                "track_cy": 0,
                "track_id": -1,
                "track_last_cx": 0,
                "track_last_cy": 0,
                "track_max_similary": 0,
                "track_max_code": 0,
                "track_max_custom_code": 0
            }
        ]
    ],
    "extendParams": {
        "version": "4.503",
        "flag": "pub20240113"
    },
    "createTime": "2024/09/24 16:18:10"
}`;
                updateStatsDisplay();
                showSuccessToast('配置已重置');
            });

            // 保存配置按钮
            saveBtn.addEventListener('click', function() {
                try {
                    const config = {
                        apiUrl: apiUrlInput.value,
                        timeout: timeoutInput.value,
                        requestBody: requestBodyTextarea.value
                    };
                    localStorage.setItem('apiTestConfig', JSON.stringify(config));
                    showSuccessToast('配置已保存');
                } catch (e) {
                    showErrorToast('保存失败: ' + e.message);
                }
            });

            // 复制响应按钮
            copyResponseBtn.addEventListener('click', function() {
                navigator.clipboard.writeText(responseBodyPre.textContent)
                    .then(() => showSuccessToast('响应内容已复制'))
                    .catch(err => showErrorToast('复制失败: ' + err.message));
            });

            // 发送请求按钮
            sendRequestBtn.addEventListener('click', function() {
                // 验证输入
                if (!apiUrlInput.value.trim()) {
                    return showErrorToast('请输入API URL');
                }

                try {
                    JSON.parse(requestBodyTextarea.value.trim() || '{}');
                } catch (e) {
                    return showErrorToast('JSON格式错误: ' + e.message);
                }

                // 更新UI状态
                sendRequestBtn.disabled = true;
                sendRequestBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i> 发送中...';
                responseStatusDiv.textContent = '请求中...';
                responseStatusDiv.className = 'text-xs px-2 py-1 rounded bg-blue-100 text-blue-800';
                responseBodyPre.textContent = '正在发送请求...';

                // 记录开始时间
                const startTime = performance.now();

                // 发送请求
                fetch(apiUrlInput.value, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json; charset=UTF-8'
                    },
                    body: requestBodyTextarea.value.trim() || '{}',
                    timeout: parseInt(timeoutInput.value) || 10000
                })
                .then(response => {
                    // 记录结束时间
                    const endTime = performance.now();
                    const responseTime = Math.round(endTime - startTime);
                    
                    // 更新统计数据
                    stats.totalRequests++;
                    stats.totalResponseTime += responseTime;
                    
                    // 显示响应头
                    let headersText = '响应头:\n';
                    for (const [key, value] of response.headers.entries()) {
                        headersText += `${key}: ${value}\n`;
                    }
                    responseHeadersDiv.textContent = headersText;
                    
                    // 处理响应状态
                    if (response.ok) {
                        stats.successRequests++;
                        responseStatusDiv.textContent = `成功 (${response.status})`;
                        responseStatusDiv.className = 'text-xs px-2 py-1 rounded bg-green-100 text-green-800';
                    } else {
                        stats.failedRequests++;
                        responseStatusDiv.textContent = `失败 (${response.status})`;
                        responseStatusDiv.className = 'text-xs px-2 py-1 rounded bg-red-100 text-red-800';
                    }
                    
                    // 尝试解析JSON响应
                    return response.text().then(text => {
                        try {
                            return JSON.stringify(JSON.parse(text), null, 2);
                        } catch (e) {
                            return text; // 不是有效的JSON，返回原始文本
                        }
                    });
                })
                .then(data => {
                    responseBodyPre.textContent = data;
                    updateStatsDisplay();
                    showSuccessToast('请求成功');
                })
                .catch(error => {
                    stats.failedRequests++;
                    responseStatusDiv.textContent = '请求失败';
                    responseStatusDiv.className = 'text-xs px-2 py-1 rounded bg-red-100 text-red-800';
                    responseBodyPre.textContent = `错误: ${error.message}`;
                    updateStatsDisplay();
                    showErrorToast('请求失败: ' + error.message);
                })
                .finally(() => {
                    // 恢复UI状态
                    sendRequestBtn.disabled = false;
                    sendRequestBtn.innerHTML = '<i class="fa fa-paper-plane mr-2"></i> 发送请求';
                });
            });

            // 从本地存储加载配置
            function loadConfig() {
                try {
                    const config = localStorage.getItem('apiTestConfig');
                    if (config) {
                        const parsedConfig = JSON.parse(config);
                        apiUrlInput.value = parsedConfig.apiUrl || apiUrlInput.value;
                        timeoutInput.value = parsedConfig.timeout || timeoutInput.value;
                        requestBodyTextarea.value = parsedConfig.requestBody || requestBodyTextarea.value;
                    }
                } catch (e) {
                    console.error('加载配置失败:', e);
                }
            }

            // 更新统计显示
            function updateStatsDisplay() {
                totalRequestsEl.textContent = stats.totalRequests;
                successRequestsEl.textContent = stats.successRequests;
                failedRequestsEl.textContent = stats.failedRequests;
                
                const avgTime = stats.successRequests > 0 
                    ? Math.round(stats.totalResponseTime / stats.successRequests) 
                    : 0;
                avgResponseTimeEl.textContent = `${avgTime} ms`;
            }

            // 显示成功提示
            function showSuccessToast(message) {
                successMessage.textContent = message;
                successToast.classList.remove('translate-y-20', 'opacity-0');
                successToast.classList.add('translate-y-0', 'opacity-100');
                
                setTimeout(() => {
                    successToast.classList.remove('translate-y-0', 'opacity-100');
                    successToast.classList.add('translate-y-20', 'opacity-0');
                }, 3000);
            }

            // 显示错误提示
            function showErrorToast(message) {
                errorMessage.textContent = message;
                errorToast.classList.remove('translate-y-20', 'opacity-0');
                errorToast.classList.add('translate-y-0', 'opacity-100');
                
                setTimeout(() => {
                    errorToast.classList.remove('translate-y-0', 'opacity-100');
                    errorToast.classList.add('translate-y-20', 'opacity-0');
                }, 3000);
            }
        });
    </script>
</body>
</html>    